/*
 * IntermoteM.nc -- top-level Intermote module
 *
 * Copyright (C) Andrey Vihrov <andrey.vihrov@gmail.com>, 2010
 * Copyright (C) Askar Salimbaev <ghostinshell@gmail.com>, 2010
 *
 * This file is part of Intermote.
 *
 * Intermote is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Intermote is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Intermote.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <Intermote.h>

#define MIN(a, b) ((a) < (b) ? (a) : (b))

module IntermoteM {

	provides {
		interface Intermote;
		interface SplitControl as IMControl;
	}

	uses {
		interface SplitControl[uint8_t];

		interface IMStorage as Storage;
	}
}
implementation {

	/* Read and write buffers */
	struct im_chunk rbuf, wbuf;
	im_off_t rptr = IM_CHUNK_SIZE, wptr;

	/* User supplied buffers */
	void *user_rbuf;
	const void *user_wbuf;

	size_t read_count, written_count;


	/* Local functions */

	task void complete_read (void)
	{
		/* Data has been read from local buffer */
		signal Intermote.readDone(SUCCESS, read_count);
	}

	task void complete_write (void)
	{
		/* Data has been stored in a local buffer */
		signal Intermote.writeDone(SUCCESS, written_count);
	}


	/* Intermote interface operations */

	command void Intermote.open (am_addr_t id)
	{
		call Storage.open(id);
	}

	command void Intermote.rseek (im_off_t offset)
	{
		rptr = offset % IM_CHUNK_SIZE;
		call Storage.rseek(offset - rptr);
	}

	command void Intermote.read (void *buf, size_t len)
	{
		if (rptr < IM_CHUNK_SIZE)
		{
			/* Bytes to copy */
			read_count = MIN(len, IM_CHUNK_SIZE - rptr);

			memcpy(buf, rbuf.data + rptr, read_count);
			rptr += read_count;
			post complete_read();
		}
		else
		{
			/* Need more data */
			read_count = MIN(len, IM_CHUNK_SIZE);
			user_rbuf = buf;
			call Storage.read(&rbuf);
		}
	}

	command void Intermote.write (const void *buf, size_t len)
	{
		written_count = MIN(len, IM_CHUNK_SIZE - wptr);

		memcpy(wbuf.data, buf, written_count);
		wptr += written_count;

		if (wptr < IM_CHUNK_SIZE)
			post complete_write();
		else
			call Storage.write(&wbuf);
	}

	command void Intermote.reserve (uint8_t count)
	{
		call Storage.reserve(count);
	}

	command void Intermote.drop (void)
	{
		call Storage.drop();
	}


	/* Storage events */

	event void Storage.openDone (error_t err)
	{
		if (err == SUCCESS)
		{
			rptr = IM_CHUNK_SIZE; /* The read buffer should be reloaded */
			wptr = 0; /* Empty */
		}

		signal Intermote.openDone(err);
	}

	event void Storage.rseekDone (error_t err)
	{
		if (err == SUCCESS)
			rptr = IM_CHUNK_SIZE;

		signal Intermote.rseekDone(err);
	}

	event void Storage.readDone (error_t err)
	{
		if (err == SUCCESS)
		{
			/* Copy new data */
			memcpy(user_rbuf, rbuf.data, read_count);
			rptr = read_count;
			signal Intermote.readDone(SUCCESS, read_count);
		}
		else if (err == EEOF)
			signal Intermote.readDone(SUCCESS, 0); /* 0 == EOF */
		else
			signal Intermote.readDone(err, 0);
	}

	event void Storage.writeDone (error_t err)
	{
		if (err == SUCCESS)
			wptr = 0;

		signal Intermote.writeDone(err, written_count);
	}


	/* IMControl interface operations */
	// TODO This needs more code (add EALREADY mb)
	// TODO Open default stream here

	enum {
		S_STOPPED = 0,
		S_STARTING,
		S_STARTED,
		S_STOPPING
	};

	uint8_t init_states[uniqueCount(UQ_IM_COREINIT)]; //TODO need SoftwareInit

	command error_t IMControl.start (void)
	{
		error_t	err = call SplitControl.start[0]();

		if (err == SUCCESS)
			init_states[0] = S_STARTING;
		else if (err == EALREADY)
		{
			init_states[0] = S_STARTING;
			signal SplitControl.startDone[0](SUCCESS);
		}

		return err;
	}

	default command error_t SplitControl.start[uint8_t id] (void)
	{
		return SUCCESS;
	}

	event void SplitControl.startDone[uint8_t id] (error_t err)
	{
		if (init_states[id] != S_STARTING)
			return;

		if (err != SUCCESS)
		{
			init_states[id] = S_STOPPED;
			signal IMControl.startDone(err);
		}
		else
		{
			init_states[id] = S_STARTED;

			if (++id == uniqueCount(UQ_IM_COREINIT))
			{
				/* All jobs done */
				signal IMControl.startDone(err);
			}
			else
			{
				err = call SplitControl.start[id]();
				if (err == SUCCESS)
					init_states[id] = S_STARTING;
				else if (err == EALREADY)
				{
					init_states[id] = S_STARTING;
					signal SplitControl.startDone[id](SUCCESS);
				}
				else
					signal IMControl.startDone(err);
			}
		}
	}


	command error_t IMControl.stop (void)
	{
		return FAIL;
	}

	event void SplitControl.stopDone[uint8_t id] (error_t err) { }

	event void Storage.dropDone (error_t err) {
		signal Intermote.dropDone(err);
	}
}

#undef MIN
